package com.yjz.common.restful

import android.text.TextUtils
import com.yjz.common.restful.annotation.BaseUrl
import com.yjz.common.restful.annotation.*
import java.lang.reflect.*

/**
 *作用 : 解析接口中的方法
 *作者 : yjz
 *时间 : 2025/2/11 14:03
 */
class MethodParser(private val baseUrl: String, private val method: Method) {
    // 域名
    private var domainUrl: String? = null

    // 是否使用post提价
    private var formPost: Boolean = true

    // 请求的方法 get post
    @RequestInfo.METHOD
    private var httpMethod: Int = RequestInfo.METHOD.UN_KNOW

    // 请求的路径
    private lateinit var relativeUrl: String

    // 返回类型
    private lateinit var returnType: Type

    // 请求头
    private var headers: MutableMap<String, String> = mutableMapOf()

    // 方法参数
    private var parameters: MutableMap<String, String> = mutableMapOf()

    private var replaceRelativeUrl: String? = null

    private var cacheStrategy: Int = CacheStrategy.NET_ONLY

    init {
        // 解析方法的注解信息： get,headers,post baseUrl
        parseMethodAnnotations()

        // 解析方法的返回类型
        parseMethodReturnType(method)
    }

    /**
     * 解析方法的注解
     */
    private fun parseMethodAnnotations() {
        val annotations = method.annotations
        for (annotation in annotations) {

            when (annotation) {
                is GET -> {
                    httpMethod = RequestInfo.METHOD.GET
                    // 获取注解中的值
                    relativeUrl = annotation.value
                }

                is POST -> {
                    httpMethod = RequestInfo.METHOD.POST
                    // 获取注解中的值
                    relativeUrl = annotation.value
                    formPost = annotation.formPost
                }

                is Headers -> {
                    val headersArray = annotation.value
                    //@Headers("auth-token:token", "accountId:123456")
                    for (header in headersArray) {
                        val colon = header.indexOf(":")
                        check(!(colon == 0 || colon == -1)) {
                            String.format(
                                "@headers value must be in the form [name:value] ,but found [%s]",
                                header
                            )
                        }
                        val name = header.substring(0, colon)
                        val value = header.substring(colon + 1).trim()
                        headers[name] = value
                    }
                }

                is BaseUrl -> {
                    domainUrl = annotation.value
                }

                is CacheStrategy -> {
                    cacheStrategy = annotation.value
                }

                else -> {
                    throw IllegalStateException("cannot handle method annotation:" + annotation.javaClass.toString())
                }
            }
        }

        // 如果当前的method不是GET 和 post则报错
        require((httpMethod == RequestInfo.METHOD.GET) || (httpMethod == RequestInfo.METHOD.POST)) {
            String.format("method %s must has one of GET,POST ", method.name)
        }
        if (domainUrl == null) {
            domainUrl = baseUrl
        }
    }

    /**
     * 解析方法返回类型
     */
    private fun parseMethodReturnType(method: Method) {
        if (method.returnType != JCall::class.java) {
            throw IllegalStateException(
                String.format(
                    "method %s must be type of ZCall.class",
                    method.name
                )
            )
        }
        val genericReturnType = method.genericReturnType
        if (genericReturnType is ParameterizedType) {
            val actualTypeArguments = genericReturnType.actualTypeArguments
            require(actualTypeArguments.size == 1) { "method %s can only has one generic return type" }
            val argument = actualTypeArguments[0]
            require(validateGenericType(argument)) {
                String.format("method %s generic return type must not be an unknown type. " + method.name)
            }
            returnType = argument
        } else {
            throw IllegalStateException(
                String.format(
                    "method %s must has one generic return type",
                    method.name
                )
            )
        }
    }

    /**
     * 校验返回的类型
     */
    private fun validateGenericType(type: Type): Boolean {
        /**
         * 错误实例
         *  fun test():ZCall<Any>
         *  fun test():ZCall<List<*>>
         *  fun test():ZCall<ApiInterface>
         * 正确实例
         *  fun test():ZCall<User>
         */
        // 如果指定的泛型是集合类型的，那还检查集合的泛型参数
        if (type is GenericArrayType) {
            return validateGenericType(type.genericComponentType)
        }
        // 如果指定的泛型是一个接口 也不行
        if (type is TypeVariable<*>) {
            return false
        }
        // 如果指定的泛型是一个通配符 ？extends Number 也不行
        if (type is WildcardType) {
            return false
        }
        return true
    }


    /**
     * 构建请求的信息
     */
    fun genericNewRequestInfo(method: Method, args: Array<out Any>?): RequestInfo {
        // 因为同一个接口请求，会存在请求相同请求参数不一样的情况
        // 所以参数的解析必须要在发起请求的地方创建
        parseMethodParameters(method, args)

        val requestInfo = RequestInfo()
        requestInfo.httpMethod = httpMethod
        requestInfo.formPost = formPost
        requestInfo.headers = headers
        requestInfo.cacheStrategy = cacheStrategy
        requestInfo.domainUrl = domainUrl
        requestInfo.parameters = parameters
        requestInfo.relativeUrl = replaceRelativeUrl ?: relativeUrl
        requestInfo.returnType = returnType
        return requestInfo
    }

    /**
     * 解析方法的参数
     */
    private fun parseMethodParameters(method: Method, args: Array<out Any>?) {
        // 参数可能会为空
        val args: Array<Any> = args as Array<Any>? ?: arrayOf()
        //每次调用api接口时  应该吧上一次解析到的参数清理掉，因为methodParser存在复用
        parameters.clear()
        // @Path("province") province: Int,@Filed("page") page: Int
        val parameterAnnotations = method.parameterAnnotations
        val equals = parameterAnnotations.size == args.size
        // 注解的数量和参数的数量必须是相等
        require(equals) {
            String.format(
                "arguments annotations count %s d‘ont match expect count %s",
                parameterAnnotations.size,
                args.size
            )
        }

        // args
        for (index in args.indices) {
            val annotations = parameterAnnotations[index]
            // 一个参数只能有一个注解，如果有多个注解的话，则会提示 filed can only has one annotation...
            require(annotations.size <= 1) { "filed can only has one annotation :index =$index" }
            val value = args[index]
            // 判断是否是基础类型
            require(isPrimitive(value)) { "8 basic types are supported for now,index=$index" }
            val annotation = annotations[0]
            if (annotation is Filed) {
                val key = annotation.value
                val value = args[index]
                parameters[key] = value.toString()
            } else if (annotation is Path) {
                val replaceName = annotation.value
                val replacement = value.toString()
                if (!TextUtils.isEmpty(replaceName) && !TextUtils.isEmpty(replacement)) {
                    //relativeUrl = home/{categroyId}
                    replaceRelativeUrl = relativeUrl.replace("{$replaceName}", replacement)
                }
            } else if (annotation is CacheStrategy) {
                cacheStrategy = value as Int
            } else {
                throw IllegalStateException("cannot handle parameter annotation :" + annotation.javaClass.toString())
            }
        }
    }

    private fun isPrimitive(value: Any): Boolean {
        // String
        if (value.javaClass == String::class.java) {
            return true
        }
        try {
            // int byte short long boolean char double float
            val field = value.javaClass.getField("TYPE")
            val clazz = field[null] as Class<*>
            if (clazz.isPrimitive) {
                return true
            }
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        }
        return false
    }

    companion object {
        fun parse(baseUrl: String, method: Method): MethodParser {
            return MethodParser(baseUrl, method)
        }
    }
}